package com.huluobo.a2308douyindemo.mvvm

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
import com.huluobo.a2308douyindemo.net.Const
import com.huluobo.a2308douyindemo.net.Res
import com.huluobo.a2308douyindemo.utils.UnPeekLiveData
import kotlinx.coroutines.launch
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody
import java.lang.RuntimeException
import kotlin.Exception

/**
 *  Created by LC on 2025/10/16.
 *  所有ViewModel的基准类
 */
open class BaseViewModel : ViewModel() {

    //声明所有网络请求的错误类型 Throwable代表错误或者失误,类似于Exception
    val onFailed by lazy { UnPeekLiveData<Throwable>() }

    //执行玩过请求的方法,T代表返回类型,比如请求商品列表,T就是商品列表的Bean,请求登录,T就是UserBean
    fun <T> executeNetwork(
        //s 参数名 suspend 代表当前方法是一个挂起方法,()  代表方法 ->产生的返回值 Res<T>代表当前方法返回值的类型
        s: suspend () -> Res<T>,
        //填入成功的LiveData,参数要与Res<T>中的T相符,Unit代表当前方法没有返回值
        success: (Res<T>) -> Unit,
        // (Throwable) -> Unit 这是一个方法,参数类型是Throwable
        //= 代表参数的默认值
        //onFailed::postValue
        failed: (Throwable) -> Unit = onFailed::setContentValue,
        //状态码
        okCode: Int = 200
    ) {
        //开启协程
        viewModelScope.launch {
            try {
                //调用一次s的方法,产生返回值
                val res = s.invoke()
                if (res.code == okCode) {
                    //成功的赋值
                    success(res)
                } else {
                    //失败的赋值
                    if (res.msg != null) {
                        failed(RuntimeException(res.msg))
                    } else {
                        failed(RuntimeException(res.message))
                    }
                }

            } catch (e: Exception) {
                //当产生错误的时候,调用failed方法,把异常传递给onFailed的PostValue
                failed(e)
            }
        }
    }

    //map转换body 方便上传
    fun mapToJson(map: Map<String, Any>): RequestBody {
        //将map转化为json,准备封装为body
        val json = Gson().toJson(map)
        //将json封装为body,用于上传
        return RequestBody.create(Const.MEDIA_TYPE.toMediaTypeOrNull(), json)
    }

    //对象转换body 方便上传
    fun <T> objToJson(entity: T): RequestBody {
        val json = Gson().toJson(entity)
        return RequestBody.create(Const.MEDIA_TYPE.toMediaTypeOrNull(), json)
    }

    //请求本地数据库的方法
    fun <T> executeDb(
        //dao中的增删改查方法
        s: suspend () -> T,
        //成功
        success: (T) -> Unit,
        //失败,失败有默认值,直接传递给失败的LiveData
        failed: (Throwable) -> Unit = onFailed::setContentValue
    ) {
        viewModelScope.launch {
            try {
                //成功,直接将dao中的方法的返回值添加到成功的LiveData中
                success(s.invoke())
            } catch (e: Exception) {
                //失败直接添加失败的LiveData
                failed(e)
            }
        }
    }
}